package abel.wlmr.model;

import java.util.ArrayList;
import java.util.List;

import abel.wlmr.model.type.DataIdBody;
import io.netty.buffer.ByteBuf;

/**
 * 数据单元标识
 * 数据单元
 * 数据单元
 * 数据单元
 * 
 * EC（见本附录 B.4.3.4.6.3）
 * Tp（见本附录 B.4.3.4.6.4）
 * 
 * @author Abel
 *
 */
public abstract class WlmrBasePart extends WlmrUserData {

    protected DataIdBody id = new DataIdBody();

    protected List<List<WlmrBaseUnit>> unitList = new ArrayList<>();

    /**
     * 
     */
    public WlmrBasePart() {
        super();
    }

    /**
     * @param guessedLen
     */
    public WlmrBasePart(short guessedLen) {
        super(guessedLen);
    }

    /* (non-Javadoc)
     * @see abel.wlmr.model.WlmrDataPart#getFinalLen()
     */
    @Override
    public short getFinalLen() {
        short len = DataIdBody.FIXED_LEN;

        for (List<WlmrBaseUnit> units : unitList) {
            for (WlmrBaseUnit unit : units) {
                len += unit.getFinalLen();
            }
        }

        return len;
    }

    @Override
    public ByteBuf appendBuf(ByteBuf buf) {
        int daCnt = id.getDa().addrList().size();
        if (daCnt == 0) {
            ++daCnt;
        }

        if (daCnt * id.getDt().typeList().size() < flatUnitList().size()) {
            throw new IllegalArgumentException("DA*DT !< count of unit for " + this.getClass().getName());
        }

        if (id.getDt().getAtom() == 0) {
            throw new IllegalArgumentException("id.dt.atom can't be 0");
        }

        checkUnitList(unitList);

        id.appendBuf(buf);

        for (List<WlmrBaseUnit> units : unitList) {
            for (WlmrBaseUnit unit : units) {
                unit.appendBuf(buf);
            }
        }

        return buf;
    }

    /* (non-Javadoc)
     * @see abel.wlmr.model.WlmrDataPart#getMinLength()
     */
    @Override
    public int getMinLength() {
        return DataIdBody.FIXED_LEN;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return String.format("{id:%s, unitList:%s}", id, unitList);
    }

    /**
     * @return the id
     */
    public DataIdBody getId() {
        return id;
    }

    /**
     * @param id
     *            the id to set
     */
    public void setId(DataIdBody id) {
        if (id == null) {
            throw new IllegalArgumentException("id can't be null");
        }
        this.id = id;
    }

    /**
     * Add one unit to the first DataID. The unit lying on head
     * will be pushed back one apartment.
     * 
     * @param unit
     */
    public void firstUnit(WlmrBaseUnit unit) {
        if (unit == null) {
            throw new IllegalArgumentException("first unit can't be null");
        }

        if (unitList.size() == 0) {
            List<WlmrBaseUnit> units = new ArrayList<>();
            units.add(unit);

            unitList.add(units);
        } else {
            List<WlmrBaseUnit> units = unitList.get(0);
            units.add(0, unit);
        }
    }

    /**
     * return first unit, may be null.
     * 
     * @return
     */
    public WlmrBaseUnit firstUnit() {
        return unitList.size() > 0 && unitList.get(0).size() > 0
            ? unitList.get(0).get(0) : null;
    }

    /**
     * Add new unit to specified group. May throw IAE if the group does not exist.
     * 
     * @param group
     * @param unit
     */
    public void addUnitGroup(short group, WlmrBaseUnit unit) {
        if (unit == null) {
            throw new IllegalArgumentException("specified unit can't be null");
        }
        if (unitList.size() < group) {
            throw new IllegalArgumentException("specified group does not exist");
        }

        // if group is just after the end, so just add a new group.
        if (unitList.size() == group) {
            unitList.add(new ArrayList<>());
        }

        unitList.get(group).add(unit);
    }

    /**
     * Get specified unit group. May throw IAE if the group does not exist.
     * 
     * @param group
     * @return
     */
    public List<WlmrBaseUnit> getUnitGroup(short group) {
        if (unitList.size() <= group) {
            throw new IllegalArgumentException("specified group does not exist");
        }

        return unitList.get(group);
    }

    /**
     * @return the unitList
     */
    public List<List<WlmrBaseUnit>> getUnitList() {
        return unitList;
    }

    /**
     * @param unitList
     *            the unitList to set
     */
    public void setUnitList(List<List<WlmrBaseUnit>> unitList) {
        checkUnitList(unitList);

        this.unitList = unitList;
    }

    /**
     * Package all units into one list.
     * 
     * @return
     */
    public List<WlmrBaseUnit> flatUnitList() {
        List<WlmrBaseUnit> list = new ArrayList<>();

        for (List<WlmrBaseUnit> us1 : unitList) {
            for (WlmrBaseUnit u1 : us1) {
                list.add(u1);
            }
        }

        return list;
    }

    /**
     * @param unitList
     */
    private void checkUnitList(List<List<WlmrBaseUnit>> unitList) {
        if (unitList == null) {
            throw new IllegalArgumentException("unitList can't be null");
        }
        for (List<WlmrBaseUnit> units : unitList) {
            if (units == null) {
                throw new IllegalArgumentException("units can't be null");
            }
            for (WlmrBaseUnit unit : units) {
                if (unit == null) {
                    throw new IllegalArgumentException("unit can't be null");
                }
            }
        }
    }
}
